#include <stdlib.h>
#include <iostream>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/XmlOutputter.h>
#include <cppunit/TextOutputter.h>
#ifndef ANDROID 
#include <signal.h>
#include <execinfo.h>
#include <cxxabi.h>

typedef unsigned int u32;
static void printStackTrace( FILE *out , unsigned int max_frames )
{
   u32 i;
   if(!out) out=stderr;
   if(!max_frames) max_frames=63;
   CPPUNIT_NS::stdCOut()<<"stack trace:\n";
 
   // storage array for stack trace address data
   void* addrlist[max_frames+1];
 
   // retrieve current stack addresses
   u32 addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));
 
   if ( addrlen == 0 ) 
   {
      fprintf( out, "  \n" );
      return;
   }
 
   // create readable strings to each frame.
   char** symbollist = backtrace_symbols( addrlist, addrlen );
 
    int status = 0;
   size_t funcnamesize = 1024;
    char* funcname = (char*)malloc(funcnamesize);
   // print the stack trace.
   for (i = 4; i < addrlen; i++ ){ 
     char *begin_name = 0, *begin_offset = 0, *end_offset = 0;

	// find parentheses and +address offset surrounding the mangled name:
	// ./module(function+0x15c) [0x8048a6d]
	for (char *p = symbollist[i]; *p; ++p)
	{
	    if (*p == '(')
		begin_name = p;
	    else if (*p == '+')
		begin_offset = p;
	    else if (*p == ')' && begin_offset) {
		end_offset = p;
		break;
	    }
	}

	if (begin_name && begin_offset && end_offset
	    && begin_name < begin_offset)
	{
	    *begin_name++ = '\0';
	    *begin_offset++ = '\0';
	    *end_offset = '\0';

	    // mangled name is now in [begin_name, begin_offset) and caller
	    // offset in [begin_offset, end_offset). now apply
	    // __cxa_demangle():

	    int status;
	    char* ret = abi::__cxa_demangle(begin_name,
					    funcname, &funcnamesize, &status);
	    if (status == 0) {
		funcname = ret; // use possibly realloc()-ed string
		fprintf(out, "  %s : %s+%s\n",
			symbollist[i], funcname, begin_offset);
	    }
	    else {
		// demangling failed. Output function name as a C function with
		// no arguments.
		fprintf(out, "  %s : %s()+%s\n",
			symbollist[i], begin_name, begin_offset);
	    }
	}
	else
	{
	    // couldn't parse the line? print the whole line.
	    fprintf(out, "  %s\n", symbollist[i]);
	}
  }
   free(funcname);
   free(symbollist);
}
void abortHandler(int signum, siginfo_t* sig, void* user){
#define INSERT_SIGNAL_NAME(s) \
  case s: name=#s;break;
  // associate each signal with a signal name string.
   const char* name = NULL;
   switch( signum )
   {
   INSERT_SIGNAL_NAME(SIGHUP)
   INSERT_SIGNAL_NAME(SIGINT)
   INSERT_SIGNAL_NAME(SIGQUIT)
   INSERT_SIGNAL_NAME(SIGILL)
   INSERT_SIGNAL_NAME(SIGTRAP)
   INSERT_SIGNAL_NAME(SIGABRT)
   INSERT_SIGNAL_NAME(SIGBUS)
   INSERT_SIGNAL_NAME(SIGFPE)
   INSERT_SIGNAL_NAME(SIGKILL)
   INSERT_SIGNAL_NAME(SIGUSR1)
   INSERT_SIGNAL_NAME(SIGSEGV)
   INSERT_SIGNAL_NAME(SIGUSR2)
   INSERT_SIGNAL_NAME(SIGPIPE)
   INSERT_SIGNAL_NAME(SIGALRM)
   INSERT_SIGNAL_NAME(SIGTERM)
   INSERT_SIGNAL_NAME(SIGSTKFLT)
   INSERT_SIGNAL_NAME(SIGCHLD)
   INSERT_SIGNAL_NAME(SIGCONT)
   INSERT_SIGNAL_NAME(SIGSTOP)
   INSERT_SIGNAL_NAME(SIGTSTP)
   }

   fprintf( stderr,"\n\n===============================\n\n");

   // Notify the user which signal was caught. We use printf, because this is the 
   // most basic output function. Once you get a crash, it is possible that more 
   // complex output systems like streams and the like may be corrupted. So we 
   // make the most basic call possible to the lowest level, most 
   // standard print function.
   if ( name )
      fprintf( stderr, "Caught signal %d (%s)\n", signum, name );
   else 
      fprintf( stderr, "Caught signal %d\n", signum );

   // Dump a stack trace.
   // This is the function we will be implementing next.
   printStackTrace(NULL,0);

   fprintf( stderr,"\n\n===============================\n\n");
   // If you caught one of the above signals, it is likely you just 
   // want to quit your program right now.
   exit( signum );
}
int setup_abortHandler(){
  struct sigaction sa;

  sa.sa_flags = SA_SIGINFO;
  sa.sa_sigaction = abortHandler;
  sigemptyset(&sa.sa_mask);
  
  sigaction(SIGINT,&sa,NULL);
  sigaction(SIGABRT,&sa,NULL);
  sigaction( SIGSEGV, &sa, NULL );
  sigaction( SIGBUS,  &sa, NULL );
  sigaction( SIGILL,  &sa, NULL );  
  sigaction( SIGKILL,  &sa, NULL );
  sigaction( SIGFPE,  &sa, NULL );  
  sigaction( SIGPIPE, &sa, NULL );
  
  return 0;
}

#endif
int main(int argc,char** argv){
  #ifndef ANDROID
  setup_abortHandler();
  #endif
  // Create the event manager and test controller
  CPPUNIT_NS::TestResult controller;

  // Add a listener that colllects test result
  CPPUNIT_NS::TestResultCollector result;
  controller.addListener( &result );        

  // Add a listener that print dots as test run.
  CPPUNIT_NS::BriefTestProgressListener progress;
  controller.addListener( &progress );      

  // Add the top suite to the test runner
  CPPUNIT_NS::TestRunner runner;
  runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
  runner.run( controller );

  // Print test in a compiler compatible format.
  CPPUNIT_NS::CompilerOutputter outputter( &result, CPPUNIT_NS::stdCOut() );
  outputter.write(); 

  //xml output only valid on deployed server
  /*
  std::ofstream xmlFileOut("UDKTestResults.xml");
  CPPUNIT_NS::XmlOutputter xmlOut(&result, xmlFileOut);
  xmlOut.setStyleSheet( "report.xsl" );
  xmlOut.write();*/

  std::ofstream txtFileOut("UDKTestResults.txt");
  CPPUNIT_NS::TextOutputter txtOut(&result, txtFileOut);
  txtOut.write();

  return result.wasSuccessful() ? 0 : 1;
}
